feat(java): thread and request resource scopes#368
Conversation
📝 WalkthroughWalkthroughThis PR adds THREAD and REQUEST resource scope support to ResourceRuntime, including per-thread instance caching, a scope-dispatching resolveForTask switch, dynamic scope-name error reporting via scopeWord, request-scoped build/teardown logic, updated Javadoc across scope-related files, and new tests. ChangesThread/Request scope support
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Task
participant ResourceRuntime
participant threadCache
participant workerTeardown
Task->>ResourceRuntime: resolveForTask(name)
ResourceRuntime->>ResourceRuntime: switch on ResourceScope
alt THREAD
ResourceRuntime->>threadCache: lookup (name, currentThread)
threadCache-->>ResourceRuntime: cached instance or none
ResourceRuntime->>ResourceRuntime: build instance if absent
ResourceRuntime->>workerTeardown: enqueue disposal (LIFO)
else REQUEST
ResourceRuntime->>ResourceRuntime: buildRequest(TaskScope)
ResourceRuntime->>ResourceRuntime: registers teardown on task scope
else WORKER/TASK
ResourceRuntime->>ResourceRuntime: existing resolveWorker/default build path
end
ResourceRuntime-->>Task: resolved instance
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
sdks/java/src/test/java/org/byteveda/taskito/ResourceTest.java (1)
108-224: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMissing coverage for REQUEST-scoped dependency resolution.
The new tests cover THREAD reuse/disposal and REQUEST freshness/disposal well, plus two negative dependency checks (THREAD→TASK, WORKER→THREAD). However, REQUEST is the newest and shortest-lived scope, and its most novel code path — resolving dependencies via
requestContext/buildRequest(per context snippet 3) — is untested:
- No test verifies a REQUEST factory can successfully
ctx.use(...)a WORKER/THREAD/TASK resource (positive path throughrequestContext).- No test verifies that a TASK/THREAD/WORKER factory depending on a REQUEST-scoped resource is rejected (REQUEST is shorter-lived than all of these, per the "same- or longer-lived" dependency rule).
Given
requestContext's dependency-resolution logic isn't exercised byperUse's no-op factory (ctx -> new Object()) inrequestResourceFreshPerUse, this is currently unverified.Example additions
`@Test` `@Timeout`(30) void requestFactoryCanUseTaskScopedResource(`@TempDir` Path dir) throws Exception { try (Taskito queue = Taskito.builder().url(dir.resolve("rrqok.db").toString()).open()) { queue.resource("perTask", ResourceScope.TASK, ctx -> new Object()); queue.resource("perUse", ResourceScope.REQUEST, ctx -> ctx.use("perTask")); // ... assert Resources.use("perUse") resolves without throwing } } `@Test` `@Timeout`(30) void taskFactoryCannotUseRequestScopedResource(`@TempDir` Path dir) throws Exception { try (Taskito queue = Taskito.builder().url(dir.resolve("rtrq.db").toString()).open()) { queue.resource("perUse", ResourceScope.REQUEST, ctx -> new Object()); queue.resource("perTask", ResourceScope.TASK, ctx -> ctx.use("perUse")); // ... assert ResourceException when using "perTask" } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdks/java/src/test/java/org/byteveda/taskito/ResourceTest.java` around lines 108 - 224, Missing coverage for REQUEST-scoped dependency resolution: add tests around the REQUEST path in ResourceTest to exercise requestContext/buildRequest with real dependencies. Create one positive test where a REQUEST resource (for example, the perUse registration) successfully ctx.use(...) a WORKER, THREAD, or TASK resource and assert it resolves correctly, and add a negative test showing a TASK/THREAD/WORKER factory depending on a REQUEST-scoped resource throws ResourceException. Use the existing ResourceScope, Resources.use, and queue.resource patterns in the new test methods to keep the scope-dependency rules covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@sdks/java/src/test/java/org/byteveda/taskito/ResourceTest.java`:
- Around line 108-224: Missing coverage for REQUEST-scoped dependency
resolution: add tests around the REQUEST path in ResourceTest to exercise
requestContext/buildRequest with real dependencies. Create one positive test
where a REQUEST resource (for example, the perUse registration) successfully
ctx.use(...) a WORKER, THREAD, or TASK resource and assert it resolves
correctly, and add a negative test showing a TASK/THREAD/WORKER factory
depending on a REQUEST-scoped resource throws ResourceException. Use the
existing ResourceScope, Resources.use, and queue.resource patterns in the new
test methods to keep the scope-dependency rules covered.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c3b9f19c-0d97-4fdd-94ab-85769bf8d3d5
📒 Files selected for processing (5)
sdks/java/src/main/java/org/byteveda/taskito/resources/ResourceContext.javasdks/java/src/main/java/org/byteveda/taskito/resources/ResourceRuntime.javasdks/java/src/main/java/org/byteveda/taskito/resources/ResourceScope.javasdks/java/src/main/java/org/byteveda/taskito/resources/package-info.javasdks/java/src/test/java/org/byteveda/taskito/ResourceTest.java
Summary
Extends the Java SDK's worker DI from two resource scopes to the full four-scope model of the cross-SDK contract.
ResourceScope.THREAD— one instance per worker thread, built lazily on first use, shared by every task that runs on that thread, disposed at worker shutdown. Backed by a per-nameConcurrentMap<Thread, Object>; only the owning thread touches its entry (plain get-build-put, no per-name lock — the concurrent map exists for cross-thread visibility at teardown). Disposers push onto the shared worker teardown deque, so disposal stays globally LIFO across worker- and thread-scoped instances (dependents before dependencies). If the pool retires a thread early (cached pools, autoscale), its instance is retained until worker teardown — documented, bounded by threads ever created.ResourceScope.REQUEST— built fresh on everyResources.use(), never cached, disposed with the task's LIFO teardown. N uses in one task yield N instances.ResourceContext): a factory may only depend on same-or-longer-lived resources — WORKER factories use worker resources only (error message generalized), THREAD factories use worker/thread, TASK/REQUEST factories use any scope.resource(name, scope, factory[, dispose])overloads; the annotation processor is untouched.Tests
4 new
ResourceTestcases: thread instance reused across sequential tasks on a single-thread worker (created==1, disposed once at shutdown); request resource fresh per use (two uses → distinct instances, both disposed at task end); thread factory using a task-scoped dep rejected; worker factory using a thread-scoped dep rejected. 9/9 green; full./gradlew buildgreen.Summary by CodeRabbit
New Features
Bug Fixes
Documentation